/**
 * @file EventLoop.cc
 * @brief 事件循环类的实现
 */

#include "EventLoop.hpp"
#include "Acceptor.hpp"
#include "MutexLock.hpp"
#include "TcpConnection.hpp"

#include <cstdlib>
#include <unistd.h>


namespace wd
{

// 构造函数：初始化事件循环
EventLoop::EventLoop(Acceptor & ac,MyLogger &mylogger)
: _epfd(createEpollFd())
, _eventfd(createEventFd())
, _acceptor(ac)
,_mylogger(mylogger)
, _isLooping(false)
, _evtArr(1024)//必须要加上1024
{
    addEpollReadEvent(_acceptor.fd());
    addEpollReadEvent(_eventfd);
}

EventLoop::~EventLoop()
{
    close(_epfd);
    close(_eventfd);
}

// 启动事件循环
void EventLoop::loop()
{
    _isLooping = true;
    while(_isLooping) {
        waitEpollFd();   
    }
}

// 在事件循环中运行函数对象
void EventLoop::runInLoop(Functor && f)
{
    {
    MutexLockGuard autolock(_mutex);
    _pendingFunctors.push_back(std::move(f));
    }

    wakeup();//唤醒_eventfd
}

// 等待epoll事件
void EventLoop::waitEpollFd()
{
    int nready = epoll_wait(_epfd, _evtArr.data(), _evtArr.size(), 5000);
    if(nready == -1 && errno == EINTR) {
        _mylogger.serverError("epoll_wait interrupted by signal");
        return;
    } else if(nready == -1) {
        perror("epoll_wait error");
        _mylogger.serverError("epoll_wait error");
        exit(EXIT_FAILURE);
    } else if(nready == 0) {
        printf("epoll timeout.\n");
    } else {
        //nready > 0
        for(int i = 0; i < nready; ++i) {
            int fd = _evtArr[i].data.fd;
            if(fd == _acceptor.fd()) {
                handleNewConnection();
            } else if(fd == _eventfd)  {
                handleRead();//处理读事件
                doPendingFunctors();//执行消息的发送
            }
            else {
                handleMessage(fd);
            }
        }
    }
}

// 处理新连接事件
void EventLoop::handleNewConnection()
{
    int netfd = _acceptor.accept();
    //将已经建立好的连接交给epoll进行监听
    addEpollReadEvent(netfd);

    //创建TcpConnection对象
    TcpConnectionPtr sp(new TcpConnection(netfd, this,_mylogger));
    //将EventLoop内部的三个函数对象转交给每个Tcp连接
    sp->setAllCallbacks(_onConnection,
                        _onMessage,
                        _onClose);
    //在map中保存TCP连接
    _conns[netfd] = sp;

    //连接建立时，要执行的事件处理器
    sp->handleNewConnectionCallback();
}

void EventLoop::handleMessage(int fd)
{
    //已经建立好的连接发生的读事件
    auto iter = _conns.find(fd);
    if(iter != _conns.end()) {
        //要判断该连接是否断开了
        bool isClosed = iter->second->isClosed();
        if(isClosed) {
            //断开连接了
            iter->second->handleCloseCallback();
            //epoll不再监听该fd
            delEpollReadEvent(fd);
            _conns.erase(fd);//从map中要删除该连接
        } else {
            //连接没有断开，有消息到达了
            iter->second->handleMesssageCallback();
        }
    }
}

void EventLoop::doPendingFunctors()
{
    printf(">> doPendingFunctors()\n");
    //遍历_pendingFunctors，执行每一个回调函数
    vector<Functor> tmp;
    {
    MutexLockGuard autolock(_mutex);
    tmp.swap(_pendingFunctors);//O(1)
    }

    for(auto & f : tmp) {
        f();
    }
}

int EventLoop::createEpollFd()
{
    int fd = ::epoll_create1(0);
    if(fd < 0) {
        perror("epoll_create1");
    }
    return fd;
}

void EventLoop::addEpollReadEvent(int fd)
{
    struct epoll_event ev;
    memset(&ev, 0, sizeof(ev));
    ev.data.fd = fd;
    ev.events = EPOLLIN;
    int ret = epoll_ctl(_epfd, EPOLL_CTL_ADD, fd, &ev);
    if(ret < 0) {
        perror("epoll_ctl");
    }
}

void EventLoop::delEpollReadEvent(int fd)
{
    struct epoll_event ev;
    memset(&ev, 0, sizeof(ev));
    ev.data.fd = fd;
    ev.events = EPOLLIN;
    int ret = epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, &ev);
    if(ret < 0) {
        perror("epoll_ctl");
    }

}

void EventLoop::wakeup()
{
    //往内核计数器上做一个加1的操作
    uint64_t one = 1;
    int ret = write(_eventfd, &one, sizeof(one));
    if(ret != sizeof(one)) {
        perror("write");
    }
}


int EventLoop::createEventFd()
{
    int fd = eventfd(0, 0);
    if(fd < 0) {
        perror("eventfd");
    }
    return fd;
}

void EventLoop::handleRead()
{
    uint64_t howmany = 0;
    int ret = read(_eventfd, &howmany, sizeof(howmany));
    printf("read howmany: %ld\n", howmany);
    if(ret != sizeof(howmany)) {
        perror("read");
    }
}
}//end of namespace wd
